#undef  xuser
#include"io/event_poll.hpp"

#define xuser mixc::io_event_poll::inc
#include"macro/xdebug_fail.hpp"
#include"macro/xdefer.hpp"
#include"memop/cast.hpp"
#include"utils/static_length_of.hpp"
#include<sys/unistd.h>
#include<sys/epoll.h>

namespace mixc::io_event_poll::origin{
    bstate_t event_poll::open(){
        this->close();

        if (m_fd = ::epoll_create(1024/* ignore */); m_fd < 0){
            return bstate_t::fail;
        }
        return bstate_t::success;
    }

    bstate_t event_poll::close(){
        if (xthe->is_open == false){
            return bstate_t::fail;
        }
        return ::close(m_fd) < 0 ? bstate_t::fail : bstate_t::success;
    }

    bstate_t event_poll::operation(::handler handler, int op, u32 events, inc::special_state_t special_state){
        epoll_event item{};
        auto care               = event_poll_flag_t::when_closed;
        auto real_events        = events | (1u << 31)/*只支持边沿触发*/;
        item.events             = real_events | u32(care);
        item.data.u64           = pair{ 
            .handler = u32(handler), 
            .handler_error = (real_events & u32(care)) != 0,
            .is_one_shot = (events & u32(event_poll_options_t::one_shot)) != 0,
            .is_hand_over = (special_state & inc::special_state_t::is_hand_over) != 0,
        };
        return epoll_ctl(m_fd, op, int(handler), & item) < 0 ? bstate_t::fail : bstate_t::success;
    }

    bstate_t event_poll::wait_core(u64 timeout_millisecond, wait_invokex const & callback){
        // 注意：
        // =================================================================================================
        // 如果数组元素超过一个需要考虑以下问题：
        // - 一个句柄可能触发了多个事件，而这多个事件只有前几个是有效的
        // - 当回调函数通过句柄进行读写操作时，如果遇到写入失败则会关闭套接字，而 items 中还有其他关于该句柄
        //   的事件，原则上需要移除掉，不然如果此时 event_poll::append 新添加的新套接字和之前挂断的套接字具有
        //   相同的句柄，那么将会异常访问
        epoll_event items[1];
        auto count              = epoll_wait(m_fd, items, inc::static_length_of(items), int(timeout_millisecond));
        auto foreach            = [&](uxx length){
            for(uxx i = 0; i < length; i++){
                static_assert(sizeof(inc::tcp::client_v4) == sizeof(inc::udp::p2p_v4));
                static_assert(sizeof(inc::tcp::client_v4) == sizeof(::handler));

                auto info       = inc::cast<pair>(items[i].data);
                auto handler    = ::handler(info.handler);
                auto flag       = event_poll_flag_t((u32)items[i].events);
                auto item       = event_poll_item{ .client_v4 = (inc::tcp::client_item_v4 *)& handler/* info.handler 只有 32bit，handler可能是 64bit */ };
                auto error      = flag & event_poll_flag_t::when_closed;
                auto need_close = error or (info.is_one_shot and info.is_hand_over);

                xdefer{
                    if (not need_close){
                        return;
                    }

                    switch(auto type = item.type(); type){
                    case inc::tcp::sock_type_t::ipv4_stream: item.client_v4->close(); break;
                    case inc::tcp::sock_type_t::ipv4_dgram:  item.p2p_v4->close(); break;
                    default:
                        xdebug_fail(
                            type != inc::tcp::sock_type_t::ipv4_stream and 
                            type != inc::tcp::sock_type_t::ipv4_dgram
                        );
                        break;
                    }
                };

                if (info.handler_error == false and error){
                    continue;
                }

                if (callback(item, flag) == loop_t::finish){
                    return loop_t::finish;
                }
            }

            return loop_t::go_on;
        };

        if (count < 0){
            return bstate_t::fail;
        }
        else if (foreach(uxx(count)) == loop_t::finish){
            return bstate_t::success;
        }

        while(true){
            auto count          = epoll_wait(m_fd, items, inc::static_length_of(items), 0);

            if (count <= 0){
                return bstate_t::success;
            }
            if (foreach(uxx(count)) == loop_t::finish){
                return bstate_t::success;
            }
        }
    }
}